// console.log("\u0061");
//a

// console.log("\uD842\uDFB7");
 // "𠮷"

/*
如果直接在\u后面跟上超过0xFFFF的数值（比如\u20BB7），JavaScript 会理解成\u20BB+7。
由于\u20BB是一个不可打印字符，所以只会显示一个空格，后面跟着一个7。
*/
// console.log("\u20BB7");
//7

/*
ES6 对这一点做出了改进，只要将码点放入大括号，就能正确解读该字符。 
*/

// console.log("\u{20BB7}");

// console.log("\u{41}\u{42}\u{43}"); 

/*
大括号表示法与四字节的 UTF-16 编码是等价的。

有了这种表示法之后，JavaScript 共有 6 种方法可以表示一个字符。
*/
// let hello = 123;
// hell\u{6F} // 123

// console.log('\u{1F680}' === '\uD83D\uDE80');

// console.log('\z' === 'z');  // true
// console.log('\172' === 'z'); // true
// console.log('\x7A' === 'z'); // true
// console.log('\u007A' === 'z'); // true
// console.log('\u{7A}' === 'z'); // true

/*
字符串的遍历器接口
ES6 为字符串添加了遍历器接口（详见《Iterator》一章），使得字符串可以被for...of循环遍历。
*/
// for (let codePoint of 'foo') {
//   console.log(codePoint)
// }
// "f"
// "o"
// "o"

/*
除了遍历字符串，这个遍历器最大的优点是可以识别大于0xFFFF的码点，传统的for循环无法识别这样的码点。

字符串text只有一个字符，但是for循环会认为它包含两个字符（都不可打印），而for...of循环会正确识别出这一个字符。
*/
// let text = String.fromCodePoint(0x20BB7);

// for (let i = 0; i < text.length; i++) {
//   console.log(text[i]);
// }
// // " "
// // " "

// for (let i of text) {
//   console.log(i);
// }
// // "𠮷"

/*
模板字符串
*/
//原始传统写法
// let basket = {count:1,onSale:1};
// console.log(
//   'There are <b>' + basket.count + '</b> ' +
//   'items in your basket, ' +
//   '<em>' + basket.onSale +
//   '</em> are on sale!'
// );
/*
ES6 引入了模板字符串解决这个问题。

*/
// console.log(`
//   There are <b>${basket.count}</b> items
//    in your basket, <em>${basket.onSale}</em>
//   are on sale!
// `)

/*
模板字符串（template string）是增强版的字符串，用反引号（`）标识。它可以当作普通字符串使用，也可以用来定义多行字符串，或者在字符串中嵌入变量。
模板字符串，都是用反引号表示。如果在模板字符串中需要使用反引号，则前面要用反斜杠转义
*/
// // 普通字符串
// `In JavaScript '\n' is a line-feed.`

// // 多行字符串
// `In JavaScript this is
//  not legal.`

// console.log(`string text line 1
// string text line 2`);

// // 字符串中嵌入变量
// let [name,time] = ["Bob","today"];
// `Hello ${name}, how are you ${time}?`


/*
使用模板字符串表示多行字符串，所有的空格和缩进都会被保留在输出之中
*/
// console.log(`
// <ul>
//   <li>first</li>
//   <li>second</li>
// </ul>
// `);
/*
所有模板字符串的空格和换行，都是被保留的，比如<ul>标签前面会有一个换行。如果你不想要这个换行，可以使用trim方法消除它。
*/
// console.log(`
// <ul>
//   <li>first</li>
//   <li>second</li>
// </ul>
// `.trim());

/*
模板字符串中嵌入变量，需要将变量名写在${}之中。
大括号内部可以放入任意的 JavaScript 表达式，可以进行运算，以及引用对象属性。
*/

// let x = 1;
// let y = 2;

// `${x} + ${y} = ${x + y}`
// // "1 + 2 = 3"

// `${x} + ${y * 2} = ${x + y * 2}`
// // "1 + 4 = 5"

// let obj = {x: 1, y: 2};
// `${obj.x + obj.y}`
// // "3"


/*
模板字符串之中还能调用函数。
*/
// function fn() {
//   return "Hello World";
// }

// `foo ${fn()} bar`
// foo Hello World bar

/*
如果大括号中的值不是字符串，将按照一般的规则转为字符串。比如，大括号中是一个对象，将默认调用对象的toString方法。

如果模板字符串中的变量没有声明，将报错。
*/
// 变量place没有声明
// let msg = `Hello, ${place}`
//place is not defined

/*
模板字符串甚至还能嵌套。
*/
// const tmpl = addrs => `
//   <table>
//   ${addrs.map(addr => `
//     <tr><td>${addr.first}</td></tr>
//     <tr><td>${addr.last}</td></tr>
//   `).join('')}
//   </table>
// `;
// console.log(tmpl([{first:'a0',last:'b0'},{first:'a1',last:'b1'}]));

/*
模板字符串写成了一个函数的返回值。执行这个函数，就相当于执行这个模板字符串了
*/
// let func = (name) => `Hello ${name}!`;
// func('Jack') // "Hello Jack!"

/*
通过模板字符串，生成正式模板的实例。
*/
// let data = { supplies: [ "broom", "mop", "cleaner" ] };
// let template = `
// <ul>
//   <% for(let i=0; i < data.supplies.length; i++) { %>
//     <li><%= data.supplies[i] %></li>
//   <% } %>
// </ul>
// `;


/*
标签模板
模板字符串的功能它可以紧跟在一个函数名后面，该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能（tagged template）。
*/
// alert`123`
// // 等同于
// alert(123)

/*
标签模板其实不是模板，而是函数调用的一种特殊形式。“标签”指的就是函数，紧跟在后面的模板字符串就是它的参数。

但是，如果模板字符里面有变量，就不是简单的调用了，而是会将模板字符串先处理成多个参数，再调用函数。

模板字符串前面有一个标识名tag，它是一个函数。整个表达式的返回值，就是tag函数处理模板字符串后的返回值。
*/

// let a = 5;
// let b = 10;
// let tag = (stringArr, value1, value2)=>{
//   console.log(stringArr,value1,value2);
// }
// tag`Hello ${ a + b } world ${ a * b }`;
// // 等同于
// tag(['Hello ', ' world ', ''], 15, 50);


/*
函数tag依次会接收到多个参数。

tag函数的第一个参数是一个数组，该数组的成员是模板字符串中那些没有变量替换的部分，也就是说，
变量替换只发生在数组的第一个成员与第二个成员之间、第二个成员与第三个成员之间，以此类推。
tag函数的其他参数，都是模板字符串各个变量被替换后的值。
由于本例中，模板字符串含有两个变量，因此tag会接受到value1和value2两个参数。

tag函数所有参数的实际值如下。

第一个参数：['Hello ', ' world ', '']
第二个参数: 15
第三个参数：50

*/
// function tag(stringArr, value1, value2){
//   // ...
// }

// // 等同于

// function tag(stringArr, ...values){
//   // ...
// }


/*
*/
// let total = 30;
// let msg = passthru`The total is ${total} (${total*1.05} with tax)`;

// function passthru(literals) {
//   let result = '';
//   let i = 0;

//   while (i < literals.length) {
//     result += literals[i++];
//     if (i < arguments.length) {
//       result += arguments[i];
//     }
//   }

//   return result;
// }

// msg //"The total is 30 (31.5 with tax)"
/*
passthru函数采用 rest 参数的写法如下
*/
// function passthru(literals, ...values) {
//   let output = "";
//   let index;
//   for (index = 0; index < values.length; index++) {
//     output += literals[index] + values[index];
//   }

//   output += literals[index]
//   return output;
// }

/*
“标签模板”的一个重要应用，就是过滤 HTML 字符串，防止用户输入恶意内容。
*/

// let sender = '<script>alert("abc")</script>'; 
// let message = SaferHTML`<p>${sender} has sent you a message.</p>`;

// function SaferHTML(templateData) {
//   let s = templateData[0];
//   for (let i = 1; i < arguments.length; i++) {
//     let arg = String(arguments[i]);

//     // Escape special characters in the substitution.
//     s += arg.replace(/&/g, "&amp;")
//             .replace(/</g, "&lt;")
//             .replace(/>/g, "&gt;");

//     // Don't escape special characters in the template.
//     s += templateData[i];
//   }
//   return s;
// }
// message//<p>&lt;script&gt;alert("abc")&lt;/script&gt; has sent you a message.</p>













